/**
 * @author Peter Kirn peter@createdigitalmedia.net
 * @author Naim Falandino naim@knowlogik.com
 * 
 * @description Based on Bad Logic Games' documentation of AudioTrack usage
 *              http://apistudios.com/hosted/marzec/badlogic/wordpress/?p=228
 */

package org.puredata;

import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.util.Log;

public class PdAudio {

    private static final String TAG = "PdAudio";
    private static final int SAMPLE_RATE_HZ = 44100;

    private AudioTrack audioTrack;
    private AudioRecord audioRecord;
    // TODO (pk) this buffer setting from Bad Logic is probably way too small
    short[] buffer = new short[1024];

    static {
        System.loadLibrary("pdportable");
    }

    public PdAudio() {
        int minSizeStereo = AudioTrack.getMinBufferSize(SAMPLE_RATE_HZ,
                AudioFormat.CHANNEL_CONFIGURATION_STEREO, AudioFormat.ENCODING_PCM_16BIT);

        try {
            audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLE_RATE_HZ,
                    AudioFormat.CHANNEL_CONFIGURATION_STEREO, AudioFormat.ENCODING_PCM_16BIT,
                    minSizeStereo, AudioTrack.MODE_STREAM);
            // audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLE_RATE_HZ,
            // AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT,
            // minSizeStereo / 2);
        } catch (Throwable ex) {
            Log.e(TAG, "Unable to create audioTrack");
        }

        setNativeAudioReference(audioTrack);
    }

    public void shutdown() {
        if (audioTrack.getPlayState() != AudioTrack.PLAYSTATE_STOPPED) {
            audioTrack.stop();
            audioTrack.flush();
        }
        // if (audioRecord.getRecordingState() != AudioRecord.RECORDSTATE_STOPPED) {
        // audioRecord.stop();
        // }

        audioTrack.release();
        // audioRecord.release();

        deleteNativeAudioReference(audioTrack);
    }

    public void play() {
        if (audioTrack.getPlayState() != AudioTrack.PLAYSTATE_PLAYING) {
            audioTrack.play();
        }
        // if (audioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
        // audioRecord.startRecording();
        // }
    }

    public void pause() {
        if (audioTrack.getPlayState() != AudioTrack.PLAYSTATE_STOPPED) {
            audioTrack.stop();
            audioTrack.flush();
        }
        // if (audioRecord.getRecordingState() != AudioRecord.RECORDSTATE_STOPPED) {
        // audioRecord.stop();
        // }
    }

    public void writeSamples(float[] samples) {
        fillBuffer(samples);
        audioTrack.write(buffer, 0, samples.length);
    }

    private void fillBuffer(float[] samples) {
        if (buffer.length < samples.length)
            buffer = new short[samples.length];

        for (int i = 0; i < samples.length; i++)
            buffer[i] = (short) (samples[i] * Short.MAX_VALUE);
    }

    private native void setNativeAudioReference(AudioTrack audioTrack);

    private native void deleteNativeAudioReference(AudioTrack audioTrack);

    /**
     * Make native references to the local <code>AudioTrack</code> and <code>AudioRecord</code>
     * objects.
     * 
     * @param audioTrack
     * @param audioRecord
     */
    private native void setNativeAudioReferences(AudioTrack audioTrack, AudioRecord audioRecord);

    /**
     * Remove native references to local <code>AudioTrack</code> and <code>AudioRecord</code>
     * objects.
     * 
     * @param audioTrack
     * @param audioRecord
     */
    private native void deleteNativeAudioReferences(AudioTrack audioTrack, AudioRecord audioRecord);
}
